<?php
/**
 * Interacts with EavValues* models to store info
 * @author Administrator
 *
 */

App::import('Model', 'Eav.EavValue');

class EavRecord extends EavAppModel 
{	
	var $name = 'EavRecord';
	protected $entityRecordCachingTime = "+1 day";
	function saveValues(AppModel $model,$entityId,$eavRecordId, $data, GigaFieldProperties  $FieldProperties)
	{
		foreach($data as $field => $value)
		{
			$type = $FieldProperties->getSchemaType($field);
			$this->getValuesSubModel($type)->saveValue($eavRecordId, $field, $value);
		}
	}
	
	function getValuesSubModel($type)
	{
		$valuesModel = 'EavValues'.ucfirst($type);
		$modelClass = 'Eav.'.$valuesModel;
		if(!isset($this->ValueModels[$type]))
		{
			$this->ValueModels[$type] = cri($modelClass);
		}
		
		return $this->ValueModels[$type];
	}
	
	function getRecords($entityModel, $entityIds, $fieldsInfo)
	{
		$typeToFieldIndex = array();
		foreach($fieldsInfo as $field => $properties)
		{
			$typeToFieldIndex[$properties['type']][] = $field;
		}
		
		$requiredValueModels = array_keys($typeToFieldIndex);
		
		$fields = array('EavRecord.id', 'EavRecord.entity_id');
		$conditions = array(
			'EavRecord.entity' => $entityModel->alias,
			'EavRecord.entity_id' => $entityIds
		);
		$eavRecords = $this->find('all', compact('conditions', 'fields'));
		$eavRecords = Set::combine($eavRecords, '{n}.EavRecord.id', '{n}.EavRecord.entity_id');
		$eavRecordIds = array_keys($eavRecords);
		
		$values = array();
		foreach($requiredValueModels as $type)
		{
			$valueModel = $this->getValuesSubModel($type);
			$conditions = array(
				$valueModel->alias.'.eav_record_id' => $eavRecordIds,
				$valueModel->alias.'.field' => $typeToFieldIndex[$type],
			);
			$tmpValues = $valueModel->find('all', compact('conditions'));

			foreach($tmpValues as $value)
			{
				extract($value[$valueModel->alias]);
				$entity_id = $eavRecords[$eav_record_id];
				$values[$entity_id][$field] = $value;
			}
		}
		return $values;
	}
	
	function getRecords_Experimental($entityModel, $entityIds, $fieldsInfo = false)
	{
		$records = array();
		$fields = array('EavRecord.id', 'EavRecord.entity_id');
		$entityIdsToSearch = array();
		foreach($entityIds as $entityId)
		{
			$records[$entityId] = array();
			$recordValue = array();
			$entityIdsToSearch[] = $entityId;
			$records[$entityId] = $recordValue;
		}
		if(count($entityIdsToSearch)> 0)
		{
			$values = array();
			$conditions = array(
			'EavRecord.entity' => $entityModel->alias,
			'EavRecord.entity_id' => $entityIdsToSearch
			);
			$eavRecords = $this->find('all', compact('conditions', 'fields'));
			if(!empty($eavRecords))
			{
				$eavRecords = Set::combine($eavRecords, '{n}.EavRecord.id', '{n}.EavRecord.entity_id');
				$eavRecordIds = array_keys($eavRecords);

				$valueSubModelsToQuery = Utility::getLowLevelFieldTypes();
				$valueSubModelsToQuery[] = 'date' ;
				$valueSubModelsToQuery = array_unique($valueSubModelsToQuery);
				if($fieldsInfo)
				{
					$typeToFieldIndex = array();
					foreach($fieldsInfo as $field => $properties)
					{
						$typeToFieldIndex[$properties['type']][] = $field;
					}
						
					$valueSubModelsToQuery = array_keys($typeToFieldIndex);
				}

				foreach($valueSubModelsToQuery as $type)
				{
					$valueModel = $this->getValuesSubModel($type);
					if($fieldsInfo)
					{
						// when we have information about fields, we'll shoot specific query for speed
						$conditions = array(
						$valueModel->alias.'.eav_record_id' => $eavRecordIds,
						$valueModel->alias.'.field' => $typeToFieldIndex[$type],
						);
					}
					else
					{
						// when no field info, fetch all fields on all sub models of values
						$conditions = array(
						$valueModel->alias.'.eav_record_id' => $eavRecordIds
						);
					}
					$tmpValues = $valueModel->find('all', compact('conditions'));
					foreach($tmpValues as $value)
					{
						extract($value[$valueModel->alias]);
						$entity_id = $eavRecords[$eav_record_id];
						$values[$entity_id][$field] = $value;
					}
				}
			}
			foreach($entityIdsToSearch as $entity_id)
			{
				$eavValues = array();
				if(isset($values[$entity_id]))
				{
					$eavValues = $values[$entity_id];
				}
				$records[$entity_id] = $eavValues;
			}
		}
		return $records;
	}

	/*function getConditions($field, $value, $operator, GigaFieldProperties $FieldProperties, $modelAlias)
	{
		if(!is_null($operator))
		{
			$operator = ' '.$operator;
		}
		$type = $FieldProperties->getSchemaType($field);
		
		$valuesSubModel = $this->getValuesSubModel($type);
		$valuesSubModelAlias = $valuesSubModel->alias;
		
		$valuesSubModel->bindModel(array('belongsTo' => array($this->alias)));
		
		$eavRecordConditions = array(
			'EavRecord.entity' => $modelAlias,
		);


		$conditions = array('and' => array(
			$eavRecordConditions, 
			$valuesSubModel->getFindConditions($field, $value, $operator))
		);

		$fields = array('EavRecord.entity_id');
		
		$eavRecords = $valuesSubModel->find('all', compact('conditions', 'fields'));
		$modelIds = Set::extract('{n}.EavRecord.entity_id', $eavRecords);
		
		$newConditions = array(
			$modelAlias.'.id' => $modelIds
		);
		
		return $newConditions;
	}*/
/**
 * Creates a WHERE clause by parsing given conditions array.  Used by DboSource::conditions().
 *
 * @param array $conditions Array or string of conditions
 * @param boolean $quoteValues If true, values should be quoted
 * @param Model $model A reference to the Model instance making the query
 * @return string SQL fragment
 */
	function parseConditions(AppModel $model,GigaFieldProperties $FieldPropertieObject,$conditions = array()) {
		$c = 0;
		$out = array();
		//we will support only these for now.
		$bool = array('and', 'or', 'not');
		$bool = array_combine($bool,$bool);
		if(is_array($conditions) == false)
		{
			return $conditions;
		}
		foreach ($conditions as $key => $value)
		{
			$not = null;

			if (is_array($value)) {
				$valueInsert = (
					!empty($value) &&
					(substr_count($key, '?') == count($value) || substr_count($key, ':') == count($value))
				);
			}
			$isBoolOpreator = in_array(strtolower(trim($key)), $bool);
			if (is_numeric($key) && empty($value)) {
				continue;
			} elseif (is_numeric($key) && is_string($value)) 
			{
				//case where simple string is passed in array
				$out[] = $value;
			} elseif (is_array($value) || $isBoolOpreator == true) 
			{
				if(is_numeric($key) == false && $isBoolOpreator == false)
				{
					//if we pass a array for a field.
					//@todo query for ids of records.
					$out[] = $this->getFieldCondition($model,$FieldPropertieObject,$key,$value); 
				}else
				{
					
					if(is_numeric($key))
					{
						$key = "AND";
					}
					$opreatorKey = $bool[low($key)];
					$subConditions = array();
					foreach($value as $subKey => $subValue)
					{
						$returnCondition = null;
						if(is_array($subValue))
						{
							$returnCondition = $this->parseConditions($model,$FieldPropertieObject,array($subKey => $subValue));
						}else
						{
							$returnCondition = $this->getFieldCondition($model,$FieldPropertieObject,$subKey,$subValue);
						}
						if($returnCondition)
						{
							$subConditions[] = $returnCondition;
						}
					}
					if(!isset($out[$opreatorKey]))
					{
						$out[$opreatorKey] = array();
					}
					$out[$opreatorKey] = am($out[$opreatorKey],$subConditions);
				}

			} else 
			{
					$out[] = $this->getFieldCondition($model, $FieldPropertieObject,$key, $value);
			}
		}
		return $out;
	}
/**
 * Extracts a Model.field identifier and an SQL condition operator from a string, formats
 * and inserts values, and composes them into an SQL snippet.
 *
 * @param Model $model Model object initiating the query
 * @param string $key An SQL key snippet containing a field and optional SQL operator
 * @param mixed $value The value(s) to be inserted in the string
 * @return string
 * @access private
 */
	function __parseKey(AppModel $model, $key, $value) {
		$sqlOps = array('like', 'ilike', 'or', 'not', 'in', 'between', 'regexp', 'similar to');
		$operatorMatch = '/^((' . join(')|(', $sqlOps);
		$operatorMatch .= '\\x20)|<[>=]?(?![^>]+>)\\x20?|[>=!]{1,3}(?!<)\\x20?)/is';
		$bound = (strpos($key, '?') !== false || (is_array($value) && strpos($key, ':') !== false));

		if (!strpos($key, ' ')) {
			//ie simple field is passed with no opreator
			$operator = null;
		} else {
			list($key, $operator) = explode(' ', trim($key), 2);

			if (!preg_match($operatorMatch, trim($operator)) && strpos($operator, ' ') !== false) {
				$key = $key . ' ' . $operator;
				$split = strrpos($key, ' ');
				$operator = substr($key, $split);
				$key = substr($key, 0, $split);
			}
		}
		$null = ($value === null || (is_array($value) && empty($value)));
		if (!preg_match($operatorMatch, $operator)) {
			$operator =  null;
		}
		$operator = trim($operator);
		return array('field'=>$key,'opreator'=>$operator,'value'=>$value);
	}
	
	
	function getFieldCondition(AppModel $model,GigaFieldProperties $FieldPropertieObject,$key, $value)
	{
		//now make condition.
		extract($this->__parseKey($model,$key,$value));
		$condition = array();
		$space = " ";
		if($opreator == null)
		{
			$space = "";
		}
		$extractedField = str_replace($model->alias.'.',"",$field);
		if($FieldPropertieObject->isField($extractedField) == false)
		{
			$condition = array($field.$space.$opreator => $value);
		}else
		{
			$field = $extractedField;
			$condition = null;
			$type= $FieldPropertieObject->getSchemaType($field);
			$eavRecords = $this->getValuesSubModel($type)->getConditionRecords($field,$value,$opreator,$model->alias);
			$entityIds = Set::extract('{n}.'.$this->alias.'.entity_id',$eavRecords);
			$condition = array($model->escapeField($model->primaryKey)=>$entityIds);
		}
		return $condition;
	}
}
?>